package github.leavesczy.reactivehttpsamples.core.http

import com.google.gson.*
import com.google.gson.reflect.TypeToken
import com.google.gson.stream.JsonReader
import com.google.gson.stream.JsonWriter
import java.io.IOException
import java.lang.NumberFormatException
import java.lang.reflect.Type

class GsonProvider private constructor() {

    companion object {
        val instance: GsonProvider by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
            GsonProvider()
        }
    }

    val gson = GsonBuilder()
            .registerTypeAdapterFactory(SafeTypeAdapterFactory())
            .registerTypeAdapter(Boolean::class.java, object : JsonDeserializer<Boolean> {

                override fun deserialize(json: JsonElement, typeOfT: Type, context: JsonDeserializationContext?): Boolean {
                    return try {
                        json.asInt == 1
                    } catch (e: NumberFormatException) {
                        json.asBoolean
                    }
                }
            })
            .create()


    /**
     * below code follow from: http://stackoverflow.com/questions/10596667/how-to-invoke-default-deserialize-with-gson
     */
    private class SafeTypeAdapterFactory: TypeAdapterFactory {

        override fun <T : Any?> create(gson: Gson, type: TypeToken<T>): TypeAdapter<T> {
            val delegate = gson.getDelegateAdapter(this, type)
            return object: TypeAdapter<T>(){

                override fun write(out: JsonWriter, value: T) {
                    try {
                        delegate.write(out, value)
                    } catch (e: Exception) {
                        delegate.write(out, null)
                    }
                }

                override fun read(inr: JsonReader): T? {
                    try {
                        return delegate.read(inr)
                    } catch (e: IOException) {
                        inr.skipValue()
                        return null
                    } catch (e: IllegalStateException) {
                        inr.skipValue()
                        return null
                    } catch (e: JsonSyntaxException) {
                        inr.skipValue()
                        var t = type.type
                        if (t is Class<*>) {
                            try {
                                return t.newInstance() as T
                            } catch (e: Exception) {
                            }
                        }
                        return null
                    } catch (e: NumberFormatException) {
                        inr.skipValue()
                        return null
                    }
                }

            }
        }
    }
}